<!--
 * @Description: Stay hungry，Stay foolish
 * @Author: ''
 * @Date: 2023-09-18 15:15:05
 * @LastEditTime: 2023-09-19 12:44:46
 * @LastEditors: ''
-->
<template>
  <div
    v-click-outside:[capture]="handleClose"
    :class="classes">
    <div
      ref="reference"
      :class="wrapClasses"
      @click="toggleVisible">
      <input :name="name" :value="currentValue" type="hidden">
      <Icon :type="arrowType" :custom="customArrowType" :size="arrowSize" :class="arrowClasses"></Icon>
      <div
        ref="input"
        :tabindex="itemDisabled ? undefined : 0"
        :class="inputClasses"
        @keydown.tab="onTab"
        @keydown.esc="onEscape"
        @keydown.up="onArrow"
        @keydown.down="onArrow"
      >
        <div :class="[prefixCls + '-color']">
          <div
            v-show="modelValue === '' && !visible"
            :class="[prefixCls + '-color-empty']">
            <i :class="[iconPrefixCls, iconPrefixCls + '-close']"></i>
          </div>
          <div
            v-show="modelValue || visible"
            :style="displayedColorStyle"></div>
        </div>
      </div>
    </div>
    <Drop
      ref="drop"
      :visible="visible"
      :placement="placement"
      :transfer="transfer"
      :classes="dropClasses"
      :eventsEnabled="eventsEnabled"
      transition-name="transition-drop"
    >
      <transition name="fade">
        <div v-if="visible" :class="[prefixCls + '-picker']">
          <div :class="[prefixCls + '-picker-wrapper']">
            <div :class="[prefixCls + '-picker-panel']">
              <Saturation
                ref="saturation"
                :value="saturationColors"
                :focused="visible"
                @change="childChange"
                @keydown.tab="handleFirstTab"
              ></Saturation>
            </div>
            <div v-if="hue" :class="[prefixCls + '-picker-hue-slider']">
              <Hue :value="saturationColors" @change="childChange"></Hue>
            </div>
            <div v-if="alpha" :class="[prefixCls + '-picker-alpha-slider']">
              <Alpha :value="saturationColors" @change="childChange"></Alpha>
            </div>
            <recommend-colors
              v-if="colors.length"
              :list="colors"
              :class="[prefixCls + '-picker-colors']"
              @picker-color="handleSelectColor"></recommend-colors>
            <recommend-colors
              v-if="!colors.length && recommend"
              :list="recommendedColor"
              :class="[prefixCls + '-picker-colors']"
              @picker-color="handleSelectColor"></recommend-colors>
          </div>
          <div :class="[prefixCls + '-confirm']">
            <span :class="confirmColorClasses">
              <template v-if="editable">
                <i-input ref="editColorInput" :modelValue="formatColor" size="small" @on-enter="handleEditColor" @on-blur="handleEditColor"></i-input>
              </template>
              <template v-else>{{formatColor}}</template>
            </span>
            <i-button
              :class="[prefixCls + '-confirm-btn-cancel']"
              ref="clear"
              :tabindex="0"
              size="small"
              @click="handleClear"
              @keydown.enter="handleClear"
              @keydown.esc="closer"
            >{{t('i.datepicker.clear')}}</i-button>
            <i-button
              ref="ok"
              :tabindex="0"
              size="small"
              type="primary"
              @click="handleSuccess"
              @keydown.tab="handleLastTab"
              @keydown.enter="handleSuccess"
              @keydown.esc="closer"
            >{{t('i.datepicker.ok')}}</i-button>
          </div>
        </div>
      </transition>
    </Drop>
  </div>
</template>

<script>
// defineOptions({
//   name:'ColorPicker'
// })

import { getCurrentInstance,defineComponent, computed,ref, watch,provide } from 'vue';
import tinycolor from 'tinycolor2';

import { directive as clickOutside } from '../../directives/v-click-outside-x';
import Drop from "../../components/select/dropdown.vue";
import RecommendColors from './components/recommend-colors.vue';
import Saturation from './components/saturation.vue';
import Hue from './components/hue.vue';
import Alpha from './components/alpha.vue';
import iInput from '../input/input.vue';
import iButton from "../button/button.vue";
import Icon from "../icon/icon.vue";
import Locale from '../../mixins/locale';
import globalConfig from '../../mixins/globalConfig';
import { oneOf } from '../../utils/assist';
import mixinsForm from '../../mixins/form';
import { changeColor, toRGBAString } from './utils';

export default defineComponent({
  mixins: [ Locale, mixinsForm, globalConfig ],
  directives: { clickOutside },
  props: {
    modelValue: {
      type: String,
      default: undefined,
    },
    hue: {
      type: Boolean,
      default: true,
    },
    alpha: {
      type: Boolean,
      default: false,
    },
    recommend: {
      type: Boolean,
      default: false,
    },
    format: {
      type: String,
      validator(value) {
        return oneOf(value, ['hsl', 'hsv', 'hex', 'rgb']);
      },
      default: undefined,
    },
    colors: {
      type: Array,
      default() {
        return [];
      },
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    size: {
      validator(value) {
        // return oneOf(value, ['small', 'large', 'default']);
        return oneOf(value, ['small', 'middle', 'large', 'default'])
      },
      default () {
        const global = getCurrentInstance().appContext.config.globalProperties;
        return !global.$HALOE || global.$HALOE.size === '' ? 'default' : global.$HALOE.size;
      }
    },
    hideDropDown: {
      type: Boolean,
      default: false,
    },
    placement: {
      type: String,
      validator(value) {
        return oneOf(value, [
          'top',
          'top-start',
          'top-end',
          'bottom',
          'bottom-start',
          'bottom-end',
          'left',
          'left-start',
          'left-end',
          'right',
          'right-start',
          'right-end',
        ]);
      },
      default: 'bottom',
    },
    transfer: {
      type: Boolean,
      default () {
        const global = getCurrentInstance().appContext.config.globalProperties;
        return !global.$HALOE || global.$HALOE.transfer === '' ? false : global.$HALOE.transfer;
      }
    },
    name: {
      type: String,
      default: undefined,
    },
    editable: {
      type: Boolean,
      default: true
    },
    // 4.0.0
    capture: {
      type: Boolean,
      default () {
        const global = getCurrentInstance().appContext.config.globalProperties;
        return !global.$HALOE ? true : global.$HALOE.capture;
      }
    },
    transferClassName: {
      type: String
    },
    // 4.6.0
    eventsEnabled: {
      type: Boolean,
      default: false
    }
  },
  name:'ColorPicker',
  components: { Drop, RecommendColors, Saturation, Hue, Alpha, iInput, iButton, Icon },
  emits: ['on-active-change', 'on-open-change', 'on-change', 'on-pick-success', 'on-pick-clear', 'update:modelValue'],

  setup(props,{emit}){
    let val=ref(changeColor(props.modelValue || ''));
    let drop=ref();
    let editColorInput=ref();
    let input=ref();
    let oldHue=ref();
    let ok=ref();
    let saturation=ref();
    const { proxy } = getCurrentInstance();
    let currentValue= props.modelValue || '';
    let dragging=ref(false);
    let visible=ref(false)
    let recommendedColor=ref([
      '#2d8cf0',
      '#19be6b',
      '#ff9900',
      '#ed4014',
      '#00b5ff',
      '#19c919',
      '#f9e31c',
      '#ea1a1a',
      '#9b1dea',
      '#00c2b1',
      '#ac7a33',
      '#1d35ea',
      '#8bc34a',
      '#f16b62',
      '#ea4ca3',
      '#0d94aa',
      '#febd79',
      '#5d4037',
      '#00bcd4',
      '#f06292',
      '#cddc39',
      '#607d8b',
      '#000000',
      '#ffffff',
    ])
    const prefixCls=ref('haloe-color-picker')
    const inputPrefixCls=ref('haloe-input')
    const iconPrefixCls=ref('haloe-icon')
    const transferPrefixCls=ref('haloe-transfer')
    
    const arrowClasses=computed(()=>{
      return [
        `${inputPrefixCls.value}-icon`,
        `${inputPrefixCls.value}-icon-normal`,
      ]
    })

    const transition=computed(()=> {
      return oneOf(props.placement, ['bottom-start', 'bottom', 'bottom-end']) ? 'slide-up' : 'fade';
    })

    const saturationColors=computed({
      get () {
        return val.value;
      },
      set (newVal) {
        val.value = newVal;
        emit('on-active-change', formatColor.value);
      }
    })

    const formatColor=computed(()=> {
      // const { format, saturationColors } = this;

      if (props.format) {
        if (props.format === 'hsl') {
          return tinycolor(saturationColors.value.hsl).toHslString();
        }

        if (props.format === 'hsv') {
          return tinycolor(saturationColors.value.hsv).toHsvString();
        }

        if (props.format === 'hex') {
          return saturationColors.value.hex;
        }

        if (props.format === 'rgb') {
          return toRGBAString(saturationColors.value.rgba);
        }
      } else if (props.alpha) {
        return toRGBAString(saturationColors.value.rgba);
      }

      return saturationColors.value.hex;
    })

    const classes= computed(()=> {
      return [
        `${prefixCls.value}`,
        {
          [`${prefixCls.value}-transfer`]: props.transfer,
        },
      ];
    })
    const inputClasses=computed(()=> {
      return [
        `${prefixCls.value}-input`,
        `${inputPrefixCls.value}`,
        `${inputPrefixCls.value}-${props.size}`,
        {
          [`${prefixCls.value}-focused`]: visible.value,
          [`${prefixCls.value}-disabled`]: proxy.itemDisabled,
        },
      ];
    })
    const wrapClasses=computed(()=> {
      return [
        `${prefixCls.value}-rel`,
        `${prefixCls.value}-${props.size}`,
        `${inputPrefixCls.value}-wrapper`,
        `${inputPrefixCls.value}-wrapper-${props.size}`,
        {
          [`${prefixCls.value}-disabled`]: proxy.itemDisabled,
        },
      ];
    })
    const dropClasses=computed(()=> {
      return {
        [`${transferPrefixCls.value}-no-max-height`]: true,
        [`${prefixCls.value}-transfer`]: props.transfer,
        [`${prefixCls.value}-hide-drop`]: props.hideDropDown,
        [props.transferClassName]: props.transferClassName
      };
    })

    const displayedColorStyle=computed(()=> {
      return { backgroundColor: toRGBAString(visible.value ? saturationColors.value.rgba : tinycolor(props.modelValue).toRgb()) };
    })

    const confirmColorClasses=computed(()=> {
      return [
        `${prefixCls.value}-confirm-color`,
        {
          [`${prefixCls.value}-confirm-color-editable`]: props.editable
        }
      ];
    })

    // global setting customArrow 有值时，arrow 赋值空
    const arrowType=computed(()=> {
      const config = proxy.globalConfig;
      let type = 'chevron-down';

      if (config) {
        if (config.colorPicker.customArrow) {
          type = '';
        } else if (config.colorPicker.arrow) {
          type = config.colorPicker.arrow;
        }
      }
      return type;
    })

    // global setting
    const customArrowType=computed(()=> {
      const config = proxy.globalConfig;
      let type = '';

      if (config) {
        if (config.colorPicker.customArrow) {
          type = config.colorPicker.customArrow;
        }
      }
      return type;
    })

    // global setting
    const arrowSize= computed(()=> {
      const config = proxy.globalConfig;
      let size = '';

      if (config) {
        if (config.colorPicker.arrowSize) {
          size = config.colorPicker.arrowSize;
        }
      }
      return size;
    })

    watch(()=>props.modelValue,(newVal)=>{
      val.value=changeColor(newVal || '');
    })
    watch(()=>visible.value,(newVal)=>{
      val.value=changeColor(props.modelValue||'');
      drop.value[newVal?'update':'destroy']()
      emit('on-open-change', Boolean(val))
    })
    // const saturationColors=computed({
    //   get () {
    //     return val.value;
    //   },
    //   set (newVal) {
    //     val.value = newVal;
    //     emit('on-active-change', formatColor);
    //   }
    // })

    const setDragging= (value)=> {
      dragging.value = value;
    }
    const handleClose= (event)=> {
      if (visible.value) {
        if (dragging.value || event.type === 'mousedown') {
          if (editColorInput.value && event.target !== editColorInput.value.$el.querySelector('input')) {
            event.preventDefault();
          }
          return;
        }

        if (props.transfer) {
          const $el = drop.value.$refs.drop;
          // console.log('$el',$el)
          // console.log('drop.value',drop.value)
          if ($el === event.target || $el.contains(event.target)) {
            return;
          }
        }

        closer(event);
        return;
      }

      visible.value = false;
    };
    const toggleVisible =()=> {
      if (proxy.itemDisabled) {
        return;
      }

      visible.value = !visible.value;
      input.value.focus();
    }
    const colorChange= (data, _oldHue)=> {
      oldHue.value = saturationColors.value.hsl.h;
      saturationColors.value = changeColor(data, _oldHue || oldHue.value);
    }
    const childChange= (data)=> {
      // console.log('data',data)
      colorChange(data);
    }

    const closer= (event)=> {
      if (event) {
        event.preventDefault();
        event.stopPropagation();
      }

      visible.value = false;
      input.value.focus();
    }
    const handleButtons= (event, value)=> {
      currentValue = value;
      emit('update:modelValue', value);
      emit('on-change', value);
      proxy.handleFormItemChange('change', value);
      closer(event);
    }
    const handleSuccess= (event)=> {
      handleButtons(event,formatColor.value);
      emit('on-pick-success');
    }
    const handleClear =(event)=> {
      handleButtons(event, '');
      emit('on-pick-clear');
    }
    const handleSelectColor= (color)=> {
      val.value = changeColor(color);
      emit('on-active-change', formatColor.value);
    }
    const handleEditColor= (event)=> {
      const value = event.target.value;
      handleSelectColor(value);
    }
    const handleFirstTab= (event)=> {
      if (event.shiftKey) {
        event.preventDefault();
        event.stopPropagation();
        ok.value.$el.focus();
      }
    }
    const handleLastTab= (event)=> {
      if (!event.shiftKey) {
        event.preventDefault();
        event.stopPropagation();
        saturation.value.$el.focus();
      }
    }
    const onTab= (event)=> {
      if (visible.value) {
        event.preventDefault();
      }
    }
    const onEscape= (event)=> {
      if (visible.value) {
        closer(event);
      }
    }
    const onArrow= (event)=> {
      if (!visible.value) {
        event.preventDefault();
        event.stopPropagation();
        visible.value = true;
      }
    }
    const handleOnEscapeKeydown= (e)=> {
      closer(e);
    }
    const handleOnDragging=(value)=> {
      setDragging(value);
    }
    provide ('ColorPickerInstance',{
      handleOnEscapeKeydown,
      handleOnDragging
    }) 
    // handleOnEscapeKeydown
    // handleOnDragging

    return {
      val,
      drop,
      editColorInput,
      input,
      oldHue,
      ok,
      saturation,
      currentValue,
      dragging,
      visible,
      recommendedColor,
      prefixCls,
      inputPrefixCls,
      iconPrefixCls,
      transferPrefixCls,
      arrowClasses,
      transition,
      saturationColors,
      formatColor,
      classes,
      wrapClasses,
      inputClasses,
      dropClasses,
      displayedColorStyle,
      confirmColorClasses,
      arrowType,
      customArrowType,
      arrowSize,
      setDragging,
      handleClose,
      toggleVisible,
      childChange,
      colorChange,
      closer,
      handleButtons,
      handleSuccess,
      handleClear,
      handleSelectColor,
      handleEditColor,
      handleFirstTab,
      handleLastTab,
      onTab,
      onEscape,
      onArrow,
      handleOnEscapeKeydown,
      handleOnDragging
    }
  }
})

</script>

<style>

</style>